#pragma once
#include <Arbitrator.h>
#include <BWAPI.h>
#include <BWTA.h>
#include <InformationManager.h>
#include <BuildOrderManager.h>
#include <math.h>
#include <algorithm>
class BuildOrderManager;

class DefenseManager : Arbitrator::Controller<BWAPI::Unit*,double>
{

  
public:
      //////// BIDDING CONSTS TO WORK WITH OTHER MODULES
  static const int BID_STANDBY; // when standing still waiting to acquire targets (RESERE/STANDBY Mode)
  static const int BID_ACTIVE; // when heading into defensive combat/retreating
  static const int BID_HEALING_RUN; // when running away from a losing battle
  static const int BID_HEALING_IDLE; // can be taken away by MorphManager
  //////////////////////////////////////////////////
    static DefenseManager* create(Arbitrator::Arbitrator<BWAPI::Unit*,double>* arbitrator);
    static void destroy();
  class DefenseData
  {
    public:
      enum DefenseMode
      {
        RESERVE, // units just started defensive role (newly created/fallback from active duty/healing)
        STANDBY, // units which were issued choking point order
                  // functionally the same as RESERVE. Mainly to prevent repeatedly sending orders to choke 
        HEALING, // units badly wounded and need to stay put until recovered
        ACTIVE   // active units go and hunt imminent threats
      };
      DefenseData() : mode(RESERVE) { };
      DefenseMode mode;
  };

  struct DefenseMission {
  
    
    enum STATUS {
      STANDBY,    // threat newly detected, no orders given to response team
      ONGOING,    // order given, no serious casualties for our response team
      SUCCESS,    // threat is destroyed or is no longer visible
      FAILING,    // threat is not destroyed, our attackers suffering major damage
      FAILED      // threat is not destroyed, our attackers annihilated
      
    };

    STATUS status;
    BWAPI::Position location;
    std::set<BWAPI::Unit*> units;
    
    std::map<BWAPI::UnitType, int> initialComposition; // who makes up this task force?
    int initStrength;
    int initThreatSize;
    bool rebuilt;
    int id;
    DefenseMission (): initStrength(0), id (-1) {} ;
    DefenseMission (int id, BWAPI::Position location, std::set<BWAPI::Unit*> units,
                    int initThreatSize) {
        this->id = id;
        this->location = location;
        this->units     = units;
        this->status    = STANDBY;
        this->initStrength = units.size();
        this->initThreatSize = initThreatSize;
        this->rebuilt = false; // one-time flag
        for each (BWAPI::Unit* unit in units){
          this->initialComposition[unit->getType()]++;
        }
        for each (std::pair<BWAPI::UnitType, int> p in initialComposition){
          BWAPI::Broodwar->printf("Group got %d of %s", p.second, p.first.getName().c_str());
        }
    };
    
    
    // returns the number of units successfully received
    int receiveReinforcements (std::set<BWAPI::Unit*> extraUnits){
      std::set<BWAPI::Unit*>::iterator itr;
      int count=0;
      for (itr=extraUnits.begin(); itr!=extraUnits.end(); itr++){
        if ((*itr)->exists() && !BWAPI::Broodwar->self()->isEnemy((*itr)->getPlayer()) ){
          this->units.insert(*itr);
          count++;
          this->initialComposition[(*itr)->getType()]++;
        }
      }
      this->initStrength+=count;
      return count;
    }
  };

  DefenseManager(Arbitrator::Arbitrator<BWAPI::Unit*,double>* arbitrator);
  void setBorderManager(BorderManager* borderManager);
  virtual void onOffer(std::set<BWAPI::Unit*> units);
  virtual void onRevoke(BWAPI::Unit* unit, double bid);
  void onRemoveUnit(BWAPI::Unit* unit);
  virtual void update();
  virtual std::string getName() const;
  virtual std::string getShortName() const;
  void setBuildOrderManager (BuildOrderManager* builder);
  void onExpansionCompleted(BWTA::BaseLocation* target, int priority);
  BWTA::Chokepoint* getChokePointClosestToBase (BWTA::BaseLocation* target);
  int getNumActiveThreats();
  bool isHealed (BWAPI::Unit* unit);
  bool isWounded (BWAPI::Unit* unit);
  bool shouldEscape (BWAPI::Unit* unit);
  static double getDamageMultiplier (BWAPI::Unit* attacker, BWAPI::Unit* target);
  static BWAPI::WeaponType getAppropriateWeapon(BWAPI::Unit* attacker, BWAPI::Unit* target);
private:
  int missionCounter;
  BorderManager* borderManager;
  BuildOrderManager* builder;
  Arbitrator::Arbitrator<BWAPI::Unit*,double>* arbitrator;
  std::set<BWTA::Chokepoint*> myBorder;
  std::vector<BWTA::Chokepoint*> myBorderVector;
  std::map<BWAPI::Unit*,DefenseData> defenders;
  std::set<BWAPI::Unit*> healingUnits;
  // maps from base location to scheduled build of Creep colonies at some priority
  std::map<BWTA::BaseLocation*, std::pair<int, int>> defensiveBuildSchedule;
  void buildDefensiveStructures (BWTA::BaseLocation* baseLoc, int priority);
  void updateDefensiveBuildSchedule (std::list<InformationManager::Threat> &activeThreats);

  std::map<InformationManager::Threat, DefenseMission> threatDefenseMap;
  int numActiveThreats; // number of enemy units in close proximity to our bases
  int numRecordedThreats; // the threats already ongoing

  BWAPI::Position getRandomChokePointPos (BWTA::Chokepoint* chokePoint);

  std::set<BWAPI::UnitType> getSuitableUnitTypes (InformationManager::Threat &threat);
  std::set<BWAPI::Unit*> getDefenders (std::set<BWAPI::UnitType> &types, InformationManager::Threat &threat);
  std::set<BWAPI::Unit*> getDefenders(
                    std::set<BWAPI::Unit*> &units, std::set<BWAPI::UnitType> &types, InformationManager::Threat &threat);

  void orderGroupAttack (const std::set<BWAPI::Unit*> &units, const BWAPI::Position &pos);
  void updateDefenseMissionStat (const InformationManager::Threat &threat, DefenseManager::DefenseMission &mission);
  void updateStaticDefenseFocusFire();
  void rebuildDefenseGroup (DefenseMission* mission);


 
};

extern DefenseManager* TheDefenseManager;